package com.express.gateway.manage.handler;

import com.express.gateway.common.CommonCons;
import com.express.gateway.common.DataStoreConstant;
import com.express.gateway.common.EventBusTopicConst;
import com.express.gateway.common.ResultFormat;
import com.express.gateway.common.util.StrUtil;
import com.express.gateway.manage.auth.RolesConstant;
import com.express.gateway.manage.auth.UserAuthUtil;
import com.express.gateway.manage.entity.Api;
import com.express.gateway.manage.enums.ContentTypeEnum;
import com.express.gateway.manage.enums.StatusCodeEnum;
import io.vertx.core.Vertx;
import io.vertx.core.eventbus.DeliveryOptions;
import io.vertx.core.http.HttpServerRequest;
import io.vertx.core.http.HttpServerResponse;
import io.vertx.core.json.JsonArray;
import io.vertx.core.json.JsonObject;
import io.vertx.ext.auth.User;
import io.vertx.ext.web.RoutingContext;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

import java.time.Instant;
import java.util.Optional;

import static com.express.gateway.common.CommonCons.KEY_CLUSTER;
import static com.express.gateway.common.CommonCons.NOT_FONT_HTML;

public class ApiConstructHandler {
    private static final Logger LOG = LogManager.getLogger(ApiConstructHandler.class);

    private final String vertxName;

    private final Vertx vertx;

    public ApiConstructHandler(Vertx vertx, String vertxName) {
        this.vertxName = vertxName;
        this.vertx = vertx;
    }

    /**
     * 查看所有API
     *
     * @param rct
     */
    public void apiListTemp(RoutingContext rct) {
        String appName = rct.request().getParam(CommonCons.KEY_APP_NAME);
        HttpServerResponse response = rct.response().putHeader(CommonCons.CONTENT_TYPE, ContentTypeEnum.JSON_UTF8.val());
        if (StrUtil.isNullOrEmpty(appName)) {
            response.end(ResultFormat.formatAsZero(StatusCodeEnum.C1404));
            return;
        }

        HttpServerRequest req = rct.request();
        int limit = Optional.ofNullable(req).map(r -> r.getParam("limit")).map(String::trim).map(Integer::valueOf).orElse(50);
        int offset = Optional.ofNullable(req).map(r -> r.getParam("offset")).map(String::trim).map(Integer::valueOf).orElse(0);
        JsonObject body = new JsonObject();
        body.put(CommonCons.KEY_APP_NAME, appName);
        body.put("limit", limit);
        body.put("offset", offset);

        String userName = rct.session().get(CommonCons.KEY_USER_NAME);
        vertx.eventBus().<JsonObject>request(EventBusTopicConst.FIND_API_BY_PAGE, body).onFailure(ex -> {
            LOG.error("[user : {}] 执行查询API 失败:{}", userName, ex.getMessage());
            response.end(ResultFormat.format(StatusCodeEnum.C500, ex.getMessage()));
        }).onSuccess(spiList -> {
            JsonObject result = spiList.body();
            JsonObject content = new JsonObject().put(CommonCons.KEY_APP_NAME, appName);
            vertx.eventBus().<JsonArray>request(EventBusTopicConst.DEPLOY_FIND_ONLINE_API, content).onFailure(ex -> {
                LOG.error("[user : {}] 执行查询在线API 失败:{}", userName, ex.getMessage());
                rct.reroute("/api/api_list.ftl");
            }).onSuccess(onLine -> {
                JsonArray online = onLine.body();
                result.getJsonArray(CommonCons.KEY_DATA).forEach(o -> {
                    JsonObject data = ((JsonObject) o);
                    data.put("online", online.contains(data.getString(CommonCons.KEY_API_NAME)));
                });
                rct.put(CommonCons.KEY_CONTEXT, result);
                rct.reroute("/api/api_list.ftl");
            });
        });
    }

    /**
     * 查看一个API
     *
     * @param rct
     */
    public void apiDetailTemp(RoutingContext rct) {
        String apiName = rct.request().getParam(CommonCons.KEY_NAME);
        HttpServerResponse response = rct.response().putHeader(CommonCons.CONTENT_TYPE, ContentTypeEnum.HTML_UTF8.val());
        if (StrUtil.isNullOrEmpty(apiName)) {
            rct.response().end(ResultFormat.formatAsZero(StatusCodeEnum.C1404));
            return;
        }

        String userName = rct.session().get(CommonCons.KEY_USER_NAME);
        LOG.info("[user : {}] 执行查看API {}", userName, apiName);
        vertx.eventBus().<JsonObject>request(EventBusTopicConst.GET_API, apiName).onFailure(ex -> {
            LOG.error("[user : {}] 执行查看API:{} 失败:{}", userName, apiName, ex.getMessage());
            response.end(ex.getMessage());
        }).onSuccess(res -> {
            if (res.body() == null || res.body().isEmpty()) {
                response.setStatusCode(404).end(NOT_FONT_HTML);
                return;
            }
            JsonObject body = res.body();
            String appName = body.getString(DataStoreConstant.API_APP_ID_NAME);
            JsonObject onLineConf = new JsonObject().put(CommonCons.KEY_APP_NAME, appName).put(CommonCons.KEY_API_NAME, apiName);
            findOnlineApis(rct, onLineConf, response, body);
        });
    }


    private void findOnlineApis(RoutingContext rct, JsonObject onLineConf, HttpServerResponse response, JsonObject body) {
        String userName = rct.session().get(CommonCons.KEY_USER_NAME);
        String apiName = onLineConf.getString(CommonCons.KEY_API_NAME);
        vertx.eventBus().<Boolean>request(EventBusTopicConst.DEPLOY_API_IS_ONLINE, onLineConf).onFailure(ex -> {
            LOG.error("[user : {}] 执行查看API:{} 失败:{}", userName, apiName, ex.getMessage());
            response.end(ex.getCause().toString());
        }).onSuccess(rs -> {
            JsonObject api = new JsonObject(body.getString(DataStoreConstant.API_CONTENT_NAME));
            api.put("online", rs.body());
            try {
                JsonObject config = api.getJsonObject("serverEntrance");
                if ("CUSTOM".equals(config.getString("serverType"))) {
                    JsonObject custom = config.getJsonObject("body");
                    api.put("customFactoryName", custom.getString("inFactoryName"));
                    custom.remove("inFactoryName");
                    api.put("customBody", custom.toString());
                }
            } catch (Exception e) {
                LOG.error("[user : {}] 执行查看API:{} 失败:{}", userName, apiName, e.toString());
            }
            JsonObject context = new JsonObject();
            context.put(CommonCons.KEY_API, api);
            rct.put(CommonCons.KEY_CONTEXT, context);
            rct.reroute("/api/api_detail.ftl");
        });
    }


    /**
     * 加载更新API需要的数据
     *
     * @param rct
     */
    public void apiUpdateTemp(RoutingContext rct) {
        String apiName = rct.request().getParam(CommonCons.KEY_NAME);
        rct.response().putHeader(CommonCons.CONTENT_TYPE, ContentTypeEnum.HTML_UTF8.val());
        if (StrUtil.isNullOrEmpty(apiName)) {
            rct.response().end(ResultFormat.formatAsZero(StatusCodeEnum.C1404));
            return;
        }
        String userName = rct.session().get(CommonCons.KEY_USER_NAME);
        LOG.info("[user : {}] 执行查看API {}", userName, apiName);
        vertx.eventBus().<JsonObject>request(EventBusTopicConst.GET_API, apiName).onFailure(ex -> {
            LOG.error("[user : {}] 执行查看API:{} 失败:{}", userName, apiName, ex.getMessage());
            rct.response().end(ex.getMessage());
        }).onSuccess(res -> {
            JsonObject body = res.body();
            JsonObject api = new JsonObject(body.getString(DataStoreConstant.API_CONTENT_NAME));
            if (api.isEmpty()) {
                rct.response().setStatusCode(404).end(NOT_FONT_HTML);
                return;
            }
            // 添加认证配置文件为字符串
            if (api.getValue("authOptions") != null) {
                api.put("authOptionBody", api.getJsonObject("authOptions").getJsonObject("option").toString());
            }
            // 添加前置处理器配置文件为字符串
            if (api.getValue("beforeHandlerOptions") != null) {
                api.put("beforeHandlerOptionsBody", api.getJsonObject("beforeHandlerOptions").getJsonObject("option").toString());
            }
            // 添加后置处理器配置文件为字符串
            if (api.getValue("afterHandlerOptions") != null) {
                api.put("afterHandlerOptionsBody", api.getJsonObject("afterHandlerOptions").getJsonObject("option").toString());
            }

            try {
                JsonObject config = api.getJsonObject("serverEntrance");
                if ("CUSTOM".equals(config.getString("serverType"))) {
                    JsonObject custom = api.getJsonObject("serverEntrance").getJsonObject("body");
                    api.put("customFactoryName", custom.getString("inFactoryName"));
                    custom.remove("inFactoryName");
                    custom.remove("params");
                    api.put("customBody", custom.toString());
                }
            } catch (Exception e) {
                LOG.error("[user : {}] 执行查看API:{} 失败:{}", userName, apiName, e.toString());
            }

            JsonObject context = new JsonObject();
            context.put(CommonCons.KEY_API, api);
            rct.put(CommonCons.KEY_CONTEXT, context);
            rct.reroute("/api/api_update.ftl");
        });
    }


    /**
     * 渲染一个将要修改的应用程序
     *
     * @param rct
     */
    public void apiCreateTemp(RoutingContext rct) {
        String appName = rct.request().getParam(CommonCons.KEY_NAME);
        JsonObject object = new JsonObject();
        object.put(CommonCons.KEY_APP_NAME, appName);
        object.put("apiCreateTime", Instant.now());
        rct.put(CommonCons.KEY_CONTEXT, object);
        rct.reroute("/api/api_update.ftl");
    }


    /**
     * 添加一个API
     *
     * @param rct
     */
    public void addApiAction(RoutingContext rct) {
        User user = rct.user();
        String userName = rct.session().get(CommonCons.KEY_USER_NAME);
        HttpServerResponse response = rct.response().putHeader(CommonCons.CONTENT_TYPE, ContentTypeEnum.JSON_UTF8.val());
        UserAuthUtil.isAuthorized(user, RolesConstant.WRITE, res -> {
            if (res.failed()) {
                LOG.error("[user : {}] 执行添加API 失败:{}", userName, res.cause());
                response.end(ResultFormat.format(StatusCodeEnum.C500, res.cause().getMessage()));
                return;
            }

            if (!res.result()) {
                LOG.error("[user : {}] 执行添加API 失败:未授权或者无权限", userName);
                response.end(ResultFormat.formatAsZero(StatusCodeEnum.C401));
                return;
            }
            Api api = Api.fromJson(rct.body().asJsonObject());
            api.setApiCreateTime(Instant.now());
            JsonObject param = new JsonObject();
            param.put(CommonCons.KEY_API_NAME, api.getApiName());
            param.put(CommonCons.KEY_APP_NAME, api.getAppName());
            param.put(CommonCons.KEY_API, api.toJson());
            LOG.info("[user : {}] 执行添加API:{}", userName, api.getApiName());
            vertx.eventBus().<Integer>request(EventBusTopicConst.ADD_API, param).onFailure(ex -> {
                LOG.error("[user : {}] 执行添加API 失败:{}", userName, ex.getCause());
                StatusCodeEnum code = ex.getCause().toString().contains("UNIQUE") ? StatusCodeEnum.C1406 : StatusCodeEnum.C500;
                response.end(ResultFormat.format(code, ex.getMessage()));
            }).onSuccess(rs -> {
                response.end(ResultFormat.format(StatusCodeEnum.C200, rs));
                LOG.info("[user : {}] 执行添加API 结果: {}", userName, rs);
            });
        });
    }


    /**
     * 更新一个API
     *
     * @param rct
     */
    public void updateApiAction(RoutingContext rct) {
        User user = rct.user();
        String userName = rct.session().get(CommonCons.KEY_USER_NAME);
        HttpServerResponse response = rct.response().putHeader(CommonCons.CONTENT_TYPE, ContentTypeEnum.JSON_UTF8.val());
        UserAuthUtil.isAuthorized(user, RolesConstant.WRITE, res -> {
            if (res.failed()) {
                LOG.error("[user : {}] 执行修改API 失败:{}", userName, res.cause());
                response.end(ResultFormat.format(StatusCodeEnum.C500, res.cause().getMessage()));
                return;
            }
            if (!res.result()) {
                LOG.error("[user : {}] 执行修改API 失败:未授权或者无权利", userName);
                response.end(ResultFormat.formatAsZero(StatusCodeEnum.C401));
                return;
            }

            LOG.info("[user : {}] 执行修改应用...", rct.session().<String>get(CommonCons.KEY_USER_NAME));
            Api dto = Api.fromJson(rct.body().asJsonObject());
            if (dto.getApiCreateTime() == null) {
                dto.setApiCreateTime(Instant.now());
            }
            JsonObject param = new JsonObject().put(CommonCons.KEY_APP_NAME, dto.getApiName()).put(CommonCons.KEY_API, dto.toJson());
            vertx.eventBus().<Integer>request(EventBusTopicConst.UPD_API, param).onFailure(ex -> {
                LOG.error("[user : {}] 执行修改API 失败:{}", userName, ex.getCause());
                response.end(ResultFormat.format(StatusCodeEnum.C500, ex.getMessage()));
            }).onSuccess(rs -> {
                response.end(ResultFormat.format(StatusCodeEnum.C200, rs));
                LOG.info("[user : {}] 执行修改API:{} 结果: {}", userName, rs, dto.getApiName());
            });
        });
    }

    /**
     * 删除一个API
     *
     * @param rct
     */
    public void delApiAction(RoutingContext rct) {
        String apiName = rct.request().getParam(CommonCons.KEY_API_NAME);
        String appName = rct.request().getParam(CommonCons.KEY_APP_NAME);
        HttpServerResponse response = rct.response().putHeader(CommonCons.CONTENT_TYPE, ContentTypeEnum.JSON_UTF8.val());
        if (StrUtil.isNullOrEmpty(appName, apiName)) {
            response.end(ResultFormat.formatAsZero(StatusCodeEnum.C1400));
            return;
        }
        User user = rct.user();
        String userName = rct.session().get(CommonCons.KEY_USER_NAME);
        UserAuthUtil.isAuthorized(user, RolesConstant.WRITE, res -> {
            if (res.failed()) {
                LOG.error("[user : {}] 执行删除API:{} 失败:{}", userName, res.cause(), apiName);
                response.end(ResultFormat.format(StatusCodeEnum.C500, res.cause().getMessage()));
                return;
            }
            JsonObject body = new JsonObject().put(CommonCons.KEY_API_NAME, apiName).put(CommonCons.KEY_APP_NAME, appName);
            if (!res.result()) {
                LOG.error("[user : {}] 执行删除API:{} 失败:未授权或者无权限", userName, apiName);
                response.end(ResultFormat.formatAsZero(StatusCodeEnum.C401));
                return;
            }

            LOG.info("[user : {}] 执行删除API:{}...", userName, apiName);
            vertx.eventBus().<Integer>request(EventBusTopicConst.DEL_API, body).onFailure(ex -> {
                LOG.error("[user : {}] 执行删除API:{} 失败:{}", userName, apiName, ex.getCause());
                response.end(ResultFormat.format(StatusCodeEnum.C500, ex.getMessage()));
            }).onSuccess(rs -> {
                response.end(ResultFormat.format(StatusCodeEnum.C200, rs));
                LOG.info("[user : {}] 执行删除API:{} 结果: {}", userName, rs, apiName);
                if (!vertx.isClustered()) {
                    return;
                }
                vertx.eventBus().publish(KEY_CLUSTER + EventBusTopicConst.DEPLOY_API_STOP, body.copy().put(CommonCons.KEY_VERTX, vertxName));
                LOG.info("广播告诉集群环境中暂停应用:" + appName + "的" + apiName + "API");
            });
        });
    }

    /**
     * 启动所有API
     *
     * @param rct
     */
    public void startAllApiAction(RoutingContext rct) {
        String appName = rct.request().getParam(CommonCons.KEY_APP_NAME);
        HttpServerResponse response = rct.response().putHeader(CommonCons.CONTENT_TYPE, ContentTypeEnum.JSON_UTF8.val());
        if (StrUtil.isNullOrEmpty(appName)) {
            response.end(ResultFormat.formatAsZero(StatusCodeEnum.C1400));
            return;
        }
        String userName = rct.session().get(CommonCons.KEY_USER_NAME);
        LOG.info("[user : {}] 执行启动应用{}:所有API..", userName, appName);
        // 用于获取所有API的参数
        JsonObject message = new JsonObject().put(CommonCons.KEY_APP_NAME, appName);
        // 获取所有API
        vertx.eventBus().<JsonArray>request(EventBusTopicConst.FIND_API_ALL, message).onFailure(ex -> {
            LOG.error("[user : {}] 执行启动所有API 查看API:{} 失败:{}", userName, appName, ex.getCause());
            response.end(ResultFormat.format(StatusCodeEnum.C500, ex.getMessage()));
        }).onSuccess(ars -> {
            DeliveryOptions option = new DeliveryOptions();
            option.setSendTimeout(200 * 301);
            JsonObject config = new JsonObject().put(CommonCons.KEY_APP_NAME, appName).put("apis", ars);
            vertx.eventBus().<String>request(EventBusTopicConst.DEPLOY_API_START_ALL, config, option).onFailure(ex -> {
                LOG.error("[user : {}] 执行启动所有API 查看API:{} 失败:{}", userName, appName, ex.getCause());
                response.end(ResultFormat.format(StatusCodeEnum.C500, ex.getMessage()));
            }).onSuccess(rs -> {
                LOG.info("[user : {}] 执行启动应用{}:所有API 成功", userName, appName);
                response.end(ResultFormat.format(StatusCodeEnum.C200, rs));
                if (!vertx.isClustered()) {
                    return;
                }
                vertx.eventBus().publish(KEY_CLUSTER + EventBusTopicConst.DEPLOY_API_START_ALL, config.copy().put(CommonCons.KEY_VERTX, vertxName));
                LOG.info("广播通知集群环境中 应用:" + appName + ",启动所有API");
            });
        });
    }


    /**
     * 启动一个API
     *
     * @param rct
     */
    public void startApiAction(RoutingContext rct) {
        String apiName = rct.request().getParam(CommonCons.KEY_API_NAME);
        HttpServerResponse response = rct.response().putHeader(CommonCons.CONTENT_TYPE, ContentTypeEnum.JSON_UTF8.val());
        if (StrUtil.isNullOrEmpty(apiName)) {
            response.end(ResultFormat.formatAsZero(StatusCodeEnum.C1400));
            return;
        }
        String userName = rct.session().get(CommonCons.KEY_USER_NAME);
        LOG.info("[user:{}] 操作 API:{}", userName, apiName);
        vertx.eventBus().<JsonObject>request(EventBusTopicConst.GET_API, apiName).onFailure(ex -> {
            LOG.error("[user : {}] 执行查看 API:{} 失败:{}", userName, apiName, ex.getMessage());
            response.end(ResultFormat.format(StatusCodeEnum.C500, ex.getMessage()));
        }).onSuccess(res -> {
            JsonObject body = res.body();
            String appName = body.getString(DataStoreConstant.API_APP_ID_NAME);
            JsonObject api = new JsonObject(body.getString(DataStoreConstant.API_CONTENT_NAME));
            JsonObject data = new JsonObject()
                    .put(CommonCons.KEY_APP_NAME, appName)
                    .put(CommonCons.KEY_API_NAME, apiName).put(CommonCons.KEY_API, api);
            vertx.eventBus().<Integer>request(EventBusTopicConst.DEPLOY_API_START, data).onFailure(ex -> {
                LOG.error("[user:{}] 执行启动 API:{} 失败:{}", userName, apiName, ex.getMessage());
                response.end(ResultFormat.format(StatusCodeEnum.C500,  ex.getMessage()));
            }).onSuccess(rs -> {
                Integer result = rs.body();
                LOG.info("[user:{}] 执行启动 API：{}  结果:{}", userName, apiName, result);
                response.end(ResultFormat.format(StatusCodeEnum.C200, result));
                if (!vertx.isClustered()) {
                    return;
                }
                vertx.eventBus().publish(KEY_CLUSTER + EventBusTopicConst.DEPLOY_API_START, data.copy().put(CommonCons.KEY_VERTX, vertxName));
                LOG.info("广播告诉集群环境中启动应用:" + appName + "的" + apiName + "API");
            });
        });
    }

    /**
     * 启动一个API
     *
     * @param rct
     */
    public void stopApiAction(RoutingContext rct) {
        String appName = rct.request().getParam(CommonCons.KEY_APP_NAME);
        String apiName = rct.request().getParam(CommonCons.KEY_API_NAME);
        JsonObject body = new JsonObject();
        body.put(CommonCons.KEY_APP_NAME, appName);
        body.put(CommonCons.KEY_API_NAME, apiName);
        String userName = rct.session().get(CommonCons.KEY_USER_NAME);
        HttpServerResponse response = rct.response().putHeader(CommonCons.CONTENT_TYPE, ContentTypeEnum.JSON_UTF8.val());
        LOG.info("[user : {}] 执行暂停API {}", userName, apiName);
        vertx.eventBus().<Integer>request(EventBusTopicConst.DEPLOY_API_STOP, body).onFailure(ex -> {
            LOG.error("[user : {}] 执行暂停API:{} 失败:{}", userName, apiName, ex.getMessage());
            response.end(ResultFormat.format(StatusCodeEnum.C500, ex.getMessage()));
        }).onSuccess(reply -> {
            Integer result = reply.body();
            LOG.info("[user : {}] 执行暂停API:{} 结果:{}", userName, apiName, result);
            response.end(ResultFormat.format(StatusCodeEnum.C200, result));
            if (!vertx.isClustered()) {
                return;
            }
            vertx.eventBus().publish(KEY_CLUSTER + EventBusTopicConst.DEPLOY_API_STOP, body.copy().put(CommonCons.KEY_VERTX, vertxName));
            LOG.info("广播告诉集群环境中暂停应用:" + appName + "的" + apiName + "API");
        });
    }
}
